package net.sf.rose.util;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import net.sf.rose.file.util.FileUtils;
import net.sf.rose.jdbc.PageBean;

/**
 * 类说明：发送Http请求的封装类
 * @author fengjian
 * @since 2020/01/19
 */
public class HttpUtils {

    public static void main(String[] args) throws Exception {
    }

    private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            .toCharArray();

    private String url;
    private HttpURLConnection conn;
    private String boundary = null;
    private boolean needEncode = false;
    private Map<String, String> textParams = new HashMap<String, String>();
    private Map<String, File> fileParams = new HashMap<String, File>();

    /**
     * 设置要请求的服务器地址
     * @param url - 服务器地址
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * 增加一个字符串数据到form表单中
     * @param name  - 字段名称
     * @param value - 字段值
     */
    public void addTextParameter(String name, String value) {
        textParams.put(name, value);
    }

    /**
     * 获取form表单中的所有字段数据
     * @return form表单中字段数据
     */
    public Map<String, String> getTextParams() {
        return textParams;
    }

    /**
     * 增加一个文件到form表单数据中
     * @param name  - 文件名
     * @param value - 文件对象
     */
    public void addFileParameter(String name, File value) {
        fileParams.put(name, value);
    }

    /**
     * 获取form表单中的所有文件数据
     * @return form表单中的所有文件数据
     */
    public Map<String, File> getFileParams() {
        return fileParams;
    }

    // 清空所有已添加的form表单数据
    public void clearAllParameters() {
        textParams.clear();
        fileParams.clear();
    }

    /**
     * 向服务器发送Get请求
     * @return 字符串
     */
    public PageBean get() {
        PageBean page = new PageBean();
        try {
            // 建立实际的连接
            this.prepareGet();
            // 读取返回结果，并转换成字符串
            page.setData(FileUtils.toString(conn.getInputStream()));
            // 关闭连接
            conn.disconnect();
        } catch (Exception e) {
            page.setMessage(e.getMessage());
        }
        return page;
    }

    /**
     * 向服务器发送Get请求
     * @return 二进制流
     */
    public PageBean getStream() {
        PageBean page = new PageBean();
        try {
            // 建立实际的连接
            this.prepareGet();
            // 读取返回结果，并转换成字符串
            page.setData(FileUtils.toByteArray(conn.getInputStream()));
            // 关闭连接
            conn.disconnect();
        } catch (Exception e) {
            page.setMessage(e.getMessage());
        }
        return page;
    }

    /**
     * 向服务器发送Post请求
     * @return 字符串
     */
    public PageBean post() {
        PageBean page = new PageBean();
        try {
            // 建立实际的连接
            this.preparePost();
            // 读取返回结果，并转换成字符串
            page.setData(FileUtils.toString(conn.getInputStream()));
            // 关闭连接
            conn.disconnect();
        } catch (Exception e) {
            page.setMessage(e.getMessage());
        }
        return page;
    }

    /**
     * 向服务器发送Post请求
     * @return 二进制流
     */
    public PageBean postStream() {
        PageBean page = new PageBean();
        try {
            // 建立实际的连接
            this.preparePost();
            // 读取返回结果，并转换成字符串
            page.setData(FileUtils.toByteArray(conn.getInputStream()));
            // 关闭连接
            conn.disconnect();
        } catch (Exception e) {
            page.setMessage(e.getMessage());
        }
        return page;
    }

    /**
     * 建立Get请求
     * @throws Exception
     */
    private void prepareGet() throws Exception {
        // 加上URL地址中的参数
        if (textParams.size() > 0) {
            StringBuilder sb = new StringBuilder();
            for (String name : textParams.keySet()) {
                String value = textParams.get(name);
                if (needEncode) {
                    value = encode(value);
                }
                sb.append('&').append(name).append('=').append(value);
            }
            if (url.indexOf('?') != -1) {
                url = url + sb.toString();
            } else {
                url = url + '?' + sb.substring(1);
            }
        }
        URL link = new URL(url);
        // 打开和URL之间的连接
        conn = (HttpURLConnection) link.openConnection();
        conn.setRequestMethod("GET");
        // 设置通用的请求属性
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("Connection", "Keep-Alive");
        conn.setConnectTimeout(5 * 1000); // 连接超时为5秒
        conn.setUseCaches(false);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        // 建立实际的连接
        conn.connect();
    }

    /**
     * 初始化打开Http通道
     * @throws Exception - 异常
     */
    private void preparePost() throws Exception {
        URL link = new URL(url);
        conn = (HttpURLConnection) link.openConnection();
        conn.setDoOutput(true);
        conn.setUseCaches(false);
        conn.setConnectTimeout(5 * 1000); // 连接超时为5秒
        conn.setRequestMethod("POST");
        // 如果有附件上传
        if (fileParams.size() > 0) {
            StringBuffer buf = new StringBuffer("----");
            Random rand = new Random();
            for (int i = 0; i < 15; i++) {
                buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
            }
            this.boundary = buf.toString();
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            OutputStream connOutStream = new DataOutputStream(conn.getOutputStream());
            writeFileParams(connOutStream);
            writeTextParams(connOutStream);
            writesEnd(connOutStream);
        } else {
            // post参数
            StringBuilder sb = new StringBuilder();
            for (String name : textParams.keySet()) {
                sb.append(URLEncoder.encode(name, "UTF-8"));
                sb.append('=');
                sb.append(URLEncoder.encode(textParams.get(name).toString(), "UTF-8"));
            }
            byte[] bytes = sb.toString().getBytes("UTF-8");
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length", String.valueOf(bytes.length));
            conn.getOutputStream().write(bytes);
        }
    }

    /**
     * 添加要发送的普通表单数据
     * @param out - 输入流
     * @throws Exception - 异常
     */
    private void writeTextParams(OutputStream out) throws Exception {
        for (String name : textParams.keySet()) {
            String value = textParams.get(name);
            out.write(("--" + boundary + "\r\n").getBytes());
            out.write(("Content-Disposition: form-data; name=\"" + name + "\"\r\n").getBytes());
            out.write(("\r\n").getBytes());
            if (needEncode) {
                value = encode(value);
            }
            out.write((value + "\r\n").getBytes("UTF-8"));
        }
    }

    /**
     * 添加要发送的文件数据
     * @param out - 输入流
     * @throws Exception - 异常
     */
    private void writeFileParams(OutputStream out) throws Exception {
        for (String name : fileParams.keySet()) {
            File value = fileParams.get(name);

            out.write(("--" + boundary + "\r\n").getBytes());
            out.write(("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + encode(value.getName())
                    + "\"\r\n").getBytes());
            out.write(("Content-Type: " + getContentType(value) + "\r\n").getBytes());
            out.write(("Content-Transfer-Encoding: " + "binary" + "\r\n").getBytes());
            out.write(("\r\n").getBytes());

            FileInputStream inStream = new FileInputStream(value);
            int bytes = 0;
            byte[] bufferByte = new byte[1024];
            while ((bytes = inStream.read(bufferByte)) != -1) {
                out.write(bufferByte, 0, bytes);
            }
            inStream.close();
            out.write(("\r\n").getBytes());
        }
    }

    /**
     * 添加结尾数据
     * @param out - 输入流
     * @throws Exception - 异常
     */
    private void writesEnd(OutputStream out) throws Exception {
        out.write(("--" + boundary + "--" + "\r\n").getBytes());
        out.write(("\r\n").getBytes());
    }

    /**
     * 获取文件的上传类型，图片格式为image/png,image/jpg等。非图片为application/octet-stream
     * @param file- 文件
     * @return 文件类型
     * @throws Exception - 异常
     */
    private String getContentType(File file) throws Exception {
        String fileName = file.getName();
        if (fileName.endsWith(".jpg")) {
            return "image/jpeg";
        } else if (fileName.endsWith(".png")) {
            return "image/png";
        }
        return "application/octet-stream";
    }

    /**
     * 对包含中文的字符串进行转码，此为UTF-8。服务器那边要进行一次解码
     * @param value- 文本内容
     * @throws Exception - 异常
     */
    private String encode(String value) throws Exception {
        return URLEncoder.encode(value, "UTF-8");
    }

}